/*
 * @Author: Wangjun
 * @Date: 2023-09-22 11:26:25
 * @LastEditTime: 2023-10-26 08:59:29
 * @LastEditors: Wangjun
 * @Description:历史数据服务程序
 * @FilePath: \xr_node_calc\history\history.go
 * hnxr
 */
package history

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"gitee.com/haodreams/godriver/opdb"
	"gitee.com/haodreams/golib/easyurl"
	"gitee.com/haodreams/golib/logs"
	"gitee.com/haodreams/libs/config"
	"gitee.com/haodreams/libs/ds"
	"gitee.com/haodreams/libs/easy"
	"gitee.com/haodreams/libs/ee"
)

const MaxRecord = 1800

type History struct {
	BeginTime    string //回算的开始时间
	EndTime      string //回算的结束时间
	Step         int64  //步长
	TBeginTime   int64
	TEndTime     int64
	curBeginTime int64 //当前开始的位置
	curEndTime   int64 //结束的位置
	//历史数据表
	FanData     ds.Dataset[float64] //风机数据
	InvData     ds.Dataset[float64] //逆变器数据
	PvboxData   ds.Dataset[float64] //光伏箱变数据
	CircuitData ds.Dataset[float64] //集电线数据
	PlantData   ds.Dataset[float64] //场站数据

	hisModeSrc string //历史数据来源
	hisURL     string //数据地址
}

func (m *History) Setup() {
	m.hisURL = config.String("history.url")
	m.hisModeSrc = config.String("history.src")
}

/**
 * @description: 必须第一个调用
 * @param {*} beginTime
 * @param {*} endTime
 * @param {int64} step
 * @return {*}
 */
func (m *History) SetTime(beginTime, endTime string, step int64) (err error) {
	if step == 0 {
		return ee.Print(errors.New("step 参数错误"), logs.CbWarn)
	}
	m.BeginTime = beginTime
	m.EndTime = endTime
	m.TBeginTime, m.TEndTime, err = m.GetBeginEndTime()
	if err != nil {
		return ee.Print(err, logs.CbWarn)
	}
	if m.TBeginTime > m.TEndTime {
		return ee.Print(errors.New("开始时间必须小于结束时间"), logs.CbWarn)
	}

	m.Step = step
	m.FanData.Category = "fan"
	m.InvData.Category = "interver"
	m.PvboxData.Category = "pvbox"
	m.CircuitData.Category = "circuit"
	m.PlantData.Category = "plant"

	m.curBeginTime = 0
	m.curEndTime = 0

	return
}

func (m *History) GetRecordsByTime(category string, t int64) (columns []string, rows *ds.TsRecords[float64], err error) {
	if t < m.TBeginTime || t >= m.TEndTime {
		err = io.EOF
		return
	}
	//请求的时间不再片段内，则重新请求数据
	if t < m.curBeginTime || t >= m.curEndTime {
		m.curBeginTime = t
		if (m.curBeginTime + MaxRecord) >= m.TEndTime {
			m.curEndTime = m.TEndTime
		} else {
			m.curEndTime = m.curBeginTime + MaxRecord
		}
		m.RequstHistoryData()
	}
	switch category {
	case "fan":
		rows = m.FanData.GetRecordsetByTime(t)
		columns = m.FanData.Titles()
	case "interver":
		rows = m.InvData.GetRecordsetByTime(t)
		columns = m.InvData.Titles()
	case "pvbox":
		rows = m.PvboxData.GetRecordsetByTime(t)
		columns = m.PvboxData.Titles()
	case "circuit":
		rows = m.CircuitData.GetRecordsetByTime(t)
		columns = m.CircuitData.Titles()
	case "plant":
		rows = m.PlantData.GetRecordsetByTime(t)
		columns = m.PlantData.Titles()
	}
	return
}

/**
 * @description: 初始化风机数据
 * @param {[]string} titles
 * @return {*}
 */
func (m *History) FanSetup(ids []int, titles []string) {
	m.FanData.Setup(ids, titles)
	m.FanData.SetCountByTime(m.TBeginTime, m.TEndTime, m.Step)
}

/**
 * @description: 初始化逆变器数据
 * @param {[]string} titles
 * @return {*}
 */
func (m *History) InverterSetup(ids []int, titles []string) {
	m.InvData.Setup(ids, titles)
	m.InvData.SetCountByTime(m.TBeginTime, m.TEndTime, m.Step)
}

/**
 * @description: 初始化箱变数据
 * @param {[]string} titles
 * @return {*}
 */
func (m *History) PvboxSetup(ids []int, titles []string) {
	m.PvboxData.Setup(ids, titles)
	m.PvboxData.SetCountByTime(m.TBeginTime, m.TEndTime, m.Step)
}

/**
 * @description: 初始化集电线数据
 * @param {[]string} titles
 * @return {*}
 */
func (m *History) CircuitSetup(ids []int, titles []string) {
	m.CircuitData.Setup(ids, titles)
	m.CircuitData.SetCountByTime(m.TBeginTime, m.TEndTime, m.Step)
}

/**
 * @description: 初始化场站数据
 * @param {[]string} titles
 * @return {*}
 */
func (m *History) PlantSetup(ids []int, titles []string) {
	m.PlantData.Setup(ids, titles)
	m.PlantData.SetCountByTime(m.TBeginTime, m.TEndTime, m.Step)
}

/**
 * @description: 请求测点的历史数据
 * @return {*}
 */
func (m *History) RequstHistoryData() (err error) {
	//请求每个类型的历史数据
	m.RequestDatasetData(&m.FanData)
	m.RequestDatasetData(&m.InvData)
	m.RequestDatasetData(&m.PvboxData)
	m.RequestDatasetData(&m.CircuitData)
	m.RequestDatasetData(&m.PlantData)
	return
}

/**
 * @description: 请求etl的历史shuju
 * @param {string} url
 * @return {*}
 */
func (m *History) RequstHistoryEtlData(url string, dataset *ds.Dataset[float64], id int) (err error) {
	type result struct {
		Code int
		Data []*opdb.Real
	}
	data, code, err := easyurl.Get(url, nil)
	if err != nil {
		return ee.Print(err, logs.CbWarn)
	}
	if code != http.StatusOK {
		err = fmt.Errorf("status code:%d url:%s", code, url)
		return ee.Print(err, logs.CbWarn)
	}
	var r result
	err = json.Unmarshal(data, &r)
	if err != nil {
		return ee.Print(err, logs.CbWarn)
	}
	nRow := dataset.GetRowIndexFromID(id)

	now := time.Now()
	for _, row := range r.Data {
		fval := row.Value
		dataset.FastSetCell(row.Time, nRow, row.Name, &fval)
	}
	logs.Info("time used:", time.Since(now))
	return
}

func (m *History) RequestDatasetData(dataset *ds.Dataset[float64]) (err error) {
	titles := dataset.Titles()
	count := dataset.RecordsetCount()
	if len(titles) > 0 && count > 0 {
		//计算需要分配的内存大小
		dataset.SetCountByTime(m.curBeginTime, m.curEndTime, m.Step)
		beginTime := easy.FormatTime(m.curBeginTime)
		endTime := easy.FormatTime(m.curEndTime)

		//调用接口读取历史数据
		switch m.hisModeSrc {
		case "xretl": //计算组内的请求地址
			ids := dataset.GetIDs()
			for _, id := range ids {
				url := fmt.Sprintf("%s/HisData?category=%s&id=%d&columns=%s&mode=rawspan&interval=%d&beginTime=%s&endTime=%s",
					m.hisURL, dataset.Category, id, strings.Join(dataset.Titles(), ","), dataset.Step, url.QueryEscape(beginTime), url.QueryEscape(endTime))
				err = m.RequstHistoryEtlData(url, dataset, id)
				if err != nil {
					ee.Print(err, logs.CbWarn)
				}
			}
			//dataset.SaveCsv(dataset.Category + "_" + easy.FormatTime(dataset.Begin, easy.YMDHMS) + ".csv")
		}
	}
	return
}

func (m *History) GetBeginEndTime() (bt, et int64, err error) {
	bt, err = easy.ParserLocalTime64(m.BeginTime)
	if err != nil {
		return
	}
	et, err = easy.ParserLocalTime64(m.EndTime)
	if err != nil {
		return
	}
	return
}
